package cn.bugstack.ai_autoclassification_qwen.service.impl;

import cn.bugstack.ai_autoclassification_qwen.domain.tBaseType;
import cn.bugstack.ai_autoclassification_qwen.domain.tProduct;
import cn.bugstack.ai_autoclassification_qwen.dto.ClassifyResponse;
import cn.bugstack.ai_autoclassification_qwen.dto.ModelCategoryResult;
import cn.bugstack.ai_autoclassification_qwen.repository.tBaseTypeRepository;
import cn.bugstack.ai_autoclassification_qwen.repository.tProductRepository;
import cn.bugstack.ai_autoclassification_qwen.service.TProductClassificationService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.model.Media;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.stereotype.Service;
import org.springframework.util.MimeTypeUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;

import java.io.*;
import java.net.URI;
import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Slf4j
public class TProductClassificationServiceimpl implements TProductClassificationService {

    // 调用大模型的客户端对象
    private final ChatModel chatModel;

    // 商品表对应的JPA仓储
    private final tProductRepository tProductRepository;

    // 类别表对应的JPA仓储
    private final tBaseTypeRepository tBaseTypeRepository;

    private final RestTemplate restTemplate;

    private final WebClient webClient;

    // Jackson 的 JSON 解析工具，用于 把模型返回的 JSON 字符串转成 Java 对象
    private final ObjectMapper mapper = new ObjectMapper();

    public ClassifyResponse classifyProduct(Long productId) {
        log.info("productId:{}", productId);
        System.out.println("productId: " + productId);
        tProduct product = tProductRepository.findById(productId)
                .orElseThrow(() -> new RuntimeException("商品不存在: " + productId));

        byte[] imageBytes = downloadAndCompressImage(product.getImageUrl());
        System.out.println("image_url: " + product.getImageUrl());

        List<tBaseType> categories = tBaseTypeRepository.findAll();
        String categoryList = categories.stream()
                .map(c -> c.getId() + " - " + c.getCategoryName())
                .collect(Collectors.joining("\n"));

        String systemPrompt = """
                你是一个电商商品分类助手。
                你会看到一个商品的文字描述和图片，以及一个“类目列表”。
                你的任务是：结合图片和文本信息，从类目列表中选出**最合适的一个类目**。
                类目名称只用类目列表中已经有的，不要含有多余的文字。
                返回结果时必须只用 JSON 格式，不要输出多余文字。
                JSON 格式如下：
                {
                  "categoryCode": "类目编号字符串",
                  "categoryName": "类目名称字符串"
                }
                如果无法确定，请选出最接近的类目，不要返回空值。
                """;

        String userPrompt = """
                下面是一个商品，请根据图片和文本内容在给定的类目列表中选择一个最合适的类目：

                商品名称：
                %s
                
                商品英文名称：
                %s

                商品描述：
                %s

                商品图片：已作为图片内容传给你（不在文本中）

                类目列表（每一行是“编号 - 名称”）：
                %s

                请严格按照之前约定的 JSON 结构返回，不要用 Markdown 代码块，不要加注释。
                如果商品图片因为大小太大或者无法下载的情况，请在没有图片的情况下根据商品的中英文名称和描述对其进行分类，不要返回空值。
                """.formatted(
                product.getName(),
                product.getEnglish_name(),
                product.getDescription() == null ? "" : product.getDescription(),
                categoryList
        );

        Prompt prompt = new Prompt(
                List.of(
                        new SystemMessage(systemPrompt),
                        new UserMessage(
                                userPrompt,
                                List.of(new Media(MimeTypeUtils.IMAGE_JPEG,
                                        new ByteArrayResource(imageBytes)))
                        )
                )
        );

        String content = null;
        ModelCategoryResult result = null;

        try {

            // 调用模型
            ChatResponse response = chatModel.call(prompt);
            content = response.getResult().getOutput().getContent();

            // 解析 JSON
            result = mapper.readValue(content, ModelCategoryResult.class);

            // 分类成功 → 写入结果
            product.setPredictedCategoryCode(result.getCategoryCode());
            product.setPredictedCategoryName(result.getCategoryName());
            product.setClassificationError(null); // 清空旧错误

        } catch (Exception e) {

            // 原始英文错误
            String rawError = e.getMessage();

            // 转成中文可读错误
            String readableError = extractReadableError(rawError);

            product.setClassificationError(readableError);

            log.error("分类失败 productId={}，原始错误：{}", productId, rawError);
            log.error("转换后的中文错误原因：{}", readableError);

//            System.err.println("分类失败 productId=" + productId + "，原因：" + errorMsg);
        }

        tProductRepository.save(product);

        // 即使失败，也返回响应
        return ClassifyResponse.builder()
                .productId(product.getId())
                .productName(product.getName())
                .categoryCode(result != null ? result.getCategoryCode() : null)
                .categoryName(result != null ? result.getCategoryName() : null)
                .rawModelOutput(content)
                .build();
    }

    public byte[] downloadAndCompressImage(String url) {

        try {
            // 1. 用 WebClient 下载原图
            URI uri = new URI(url);
            byte[] originalBytes = webClient.get()
                    .uri(uri)
                    .retrieve()
                    .bodyToMono(byte[].class)
                    .block();

            if (originalBytes == null || originalBytes.length == 0) {
                throw new RuntimeException("图片为空: " + url);
            }

            // 2. 如果原图本身小于8MB，则无需压缩
            if (originalBytes.length < (8 * 1024 * 1024)) {
                return originalBytes;
            }

            System.out.println("压缩图片: " + url + ", 原始大小 = " + originalBytes.length / 1024 + " KB");

            // 3. 使用 Thumbnailator 压缩（统一限制最长边 1024px、质量80%）
            ByteArrayInputStream bis = new ByteArrayInputStream(originalBytes);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();

            Thumbnails.of(bis)
                    .size(1024, 1024)
                    .outputQuality(0.8)  // 0.8 是高质量，0.5 也行
                    .toOutputStream(bos);

            byte[] compressed = bos.toByteArray();

            System.out.println("压缩后大小 = " + compressed.length / 1024 + " KB");

            return compressed;

        } catch (Exception e) {
            System.out.println("图片处理失败, image_url:  " + url);
            System.out.println("异常：" + e.toString());
            throw new RuntimeException("图片处理失败: " + url, e);
        }
    }

    private String extractReadableError(String raw) {

        if (raw == null) return "未知错误";

        // 内容安全拒绝
        if (raw.contains("inappropriate content")
                || raw.contains("data_inspection_failed")) {
            return "被阿里云内容安全拦截：图片或文本可能包含不适宜内容。";
        }

        // JSON 解析失败
        if (raw.contains("JSON") || raw.contains("Unrecognized token")
                || raw.contains("Unexpected character")) {
            return "模型返回结果格式错误（JSON解析失败）。";
        }

        // 网络 / 连接异常
        if (raw.contains("Connection refused")
                || raw.contains("connect timed out")
                || raw.contains("Read timed out")) {
            return "网络连接异常：无法访问图片或模型接口。";
        }

        // 图片下载异常
        if (raw.contains("image_url")
                || raw.contains("Could not read image")
                || raw.contains("Exceeded limit on max bytes to buffer")) {
            return "商品图片下载或处理失败（图片过大或格式不正确）。";
        }

        // 模型内容为空
        if (raw.contains("Output") && raw.contains("null")) {
            return "模型未返回有效分类结果。";
        }

        // 默认
        if (raw.length() > 500) {
            raw = raw.substring(0, 500);
        }
        return "模型处理失败：" + raw;
    }

}

